﻿#region Sharp3DMath, Copyright(C) 2004 Eran Kampf, Licensed under LGPL.
//	Sharp3DMath math library
//	Copyright (C) 2003-2004  
//	Eran Kampf
//	eran@ekampf.com
//	http://www.ekampf.com
//
//	This library is free software; you can redistribute it and/or
//	modify it under the terms of the GNU Lesser General Public
//	License as published by the Free Software Foundation; either
//	version 2.1 of the License, or (at your option) any later version.
//
//	This library is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//	Lesser General Public License for more details.
//
//	You should have received a copy of the GNU Lesser General Public
//	License along with this library; if not, write to the Free Software
//	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#endregion
#region Using directives
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

using Sharp3DMath.Core;
#endregion


namespace Sharp3DMath.Geometry2D
{
	/// <summary>
	/// Provides methods for computing diatnces between geometric primitives
	/// in 2D space.
	/// </summary>
	public static class DistanceMethods
    {
        #region Point-Line
        public static float Distance(Vector2F point, double a, double b)
        {
            float x = point.X;
            float y = point.Y;
            return (float)(System.Math.Abs(a * x - y + b) / System.Math.Sqrt(a * a + 1));
        }

        
        /// <summary>
        /// Distance to Line
        /// </summary>
        /// <param name="p"></param>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static double Distance(Vector2F p, Vector2F a, Vector2F b)
        {
            if (a == b)
            {
                return Distance(p, a);
            }
            double num = ((a.X - b.X) * (a.X - b.X)) + ((a.Y - b.Y) * (a.Y - b.Y));
            double num2 = ((p.X - a.X) * (b.X - a.X)) + ((p.Y - a.Y) * (b.Y - a.Y));
            double num3 = ((double)num2) / ((double)num);
            Vector2F q = new Vector2F(a.X + ((int)(num3 * (b.X - a.X))), a.Y + ((int)(num3 * (b.Y - a.Y))));
            return Distance(p, q);
        }
        #endregion
        #region Point-Point
        /// <summary>
		/// Calculates the squared distance between two points.
		/// </summary>
		/// <param name="p0">A <see cref="Vector2F"/> instance.</param>
		/// <param name="p1">A <see cref="Vector2F"/> instance.</param>
		/// <returns>The squared distance between the two points.</returns>
		public static float SquaredDistance(Vector2F p0, Vector2F p1)
		{
			Vector2F diff = p0 - p1;
			return diff.GetLengthSquared();
		}
		/// <summary>
		/// Calculates the distance between two points.
		/// </summary>
		/// <param name="p0">A <see cref="Vector2F"/> instance.</param>
		/// <param name="p1">A <see cref="Vector2F"/> instance.</param>
		/// <returns>The distance between the two points.</returns>
		public static float Distance(Vector2F p0, Vector2F p1)
		{
			Vector2F diff = p0 - p1;
			return diff.GetLength();
		}
		#endregion

		#region Point-Segment
		/// <summary>
		/// Calculates the squared distance between a point and a segment.
		/// </summary>
		/// <param name="point">A <see cref="Vector2F"/> instance.</param>
		/// <param name="segment">A <see cref="Segment"/> instance.</param>
		/// <returns>The squared distance between the point and the segment.</returns>
		public static float SquaredDistance(Vector2F point, Segment segment)
		{
			Vector2F D = segment.P1 - segment.P0;
			Vector2F diffPointP0 = point - segment.P0;
			float t = Vector2F.DotProduct(D, diffPointP0);

			if (t <= 0)
			{
				// segment.P0 is the closest to point.
				return Vector2F.DotProduct(diffPointP0, diffPointP0);
			}

			float DdD = Vector2F.DotProduct(D, D);
			if (t >= DdD)
			{
				// segment.P1 is the closest to point.
				Vector2F diffPointP1 = point - segment.P1;
				return Vector2F.DotProduct(diffPointP1, diffPointP1);
			}
			
			// Closest point is inside the segment.
			return Vector2F.DotProduct(diffPointP0, diffPointP0) - t * t / DdD;
		}
		/// <summary>
		/// Calculates the squared distance between a point and a segment.
		/// </summary>
		/// <param name="point">A <see cref="Vector2F"/> instance.</param>
		/// <param name="segment">A <see cref="Segment"/> instance.</param>
		/// <returns>The squared distance between the point and the segment.</returns>
		public static float Distance(Vector2F point, Segment segment)
		{
			return (float)System.Math.Sqrt(SquaredDistance(point, segment));
		}
        /// <summary>
        /// Relative distance between point and line;
        /// </summary>
        /// <param name="point"></param>
        /// <param name="segment"></param>
        /// <returns></returns>
        public static float RelativeSquaredDistance(Vector2F point, Segment segment)
        {
            Vector2F D = segment.P1 - segment.P0;
            Vector2F diffPointP0 = point - segment.P0;
            float t = Vector2F.KrossProduct(D, diffPointP0);
            if (MathEx.ApproxEquals(t,0))
            {
                return 0;
            }
            t = Vector2F.DotProduct(D, diffPointP0);

            if (t <= 0)
            {
                // segment.P0 is the closest to point.
                return Vector2F.DotProduct(diffPointP0, diffPointP0);
            }

            float DdD = Vector2F.DotProduct(D, D);
            if (t >= DdD)
            {
                // segment.P1 is the closest to point.
                Vector2F diffPointP1 = point - segment.P1;
                return Vector2F.DotProduct(diffPointP1, diffPointP1);
            }

            // Closest point is inside the segment.
            return Vector2F.DotProduct(diffPointP0, diffPointP0) - t * t / DdD;
 
        }
        public static float RelativeDistance(Vector2F point, Segment segment)
        {
            return (float)System.Math.Sqrt(SquaredDistance(point, segment));
        }
		#endregion

		#region Point-Ray
		/// <summary>
		/// Calculates the squared distance between a given point and a given ray.
		/// </summary>
		/// <param name="point">A <see cref="Vector2F"/> instance.</param>
		/// <param name="ray">A <see cref="Ray"/> instance.</param>
		/// <returns>The squared distance between the point and the ray.</returns>
		public static float SquaredDistance(Vector2F point, Ray ray)
		{
			Vector2F diff = point - ray.Origin;
			float t = Vector2F.DotProduct(diff, ray.Direction);

			if (t <= 0.0f)
			{
				return diff.GetLengthSquared();
			}
			else
			{
				t = (t * t) / ray.Direction.GetLengthSquared();
				return diff.GetLengthSquared() - t;
			}
		}
		/// <summary>
		/// Calculates the distance between a given point and a given ray.
		/// </summary>
		/// <param name="point">A <see cref="Vector2F"/> instance.</param>
		/// <param name="ray">A <see cref="Ray"/> instance.</param>
		/// <returns>The distance between the point and the ray.</returns>
		public static float Distance(Vector2F point, Ray ray)
		{
			return (float)System.Math.Sqrt(SquaredDistance(point, ray));
		}
		#endregion

		#region Point-Oriented Box
		/// <summary>
		/// Calculates the squared distance between a point and an oriented box.
		/// </summary>
		/// <param name="point">A <see cref="Vector2F"/> instance.</param>
		/// <param name="box">An <see cref="OrientedBox"/> instance.</param>
		/// <returns>The squared distance between the point and the oriented box.</returns>
		public static float SquaredDistance(Vector2F point, OrientedBox box)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Calculates the distance between a point and an oriented box.
		/// </summary>
		/// <param name="point">A <see cref="Vector2F"/> instance.</param>
		/// <param name="box">An <see cref="OrientedBox"/> instance.</param>
		/// <returns>The distance between the point and the oriented box.</returns>
		public static float Distance(Vector2F point, OrientedBox box)
		{
			throw new NotImplementedException();
		}
		#endregion

		#region Ray-Ray
		/// <summary>
		/// Calculates the squared distance between two rays.
		/// </summary>
		/// <param name="r0">A <see cref="Ray"/> instance.</param>
		/// <param name="r1">A <see cref="Ray"/> instance.</param>
		/// <returns>Returns the squared distance between two rays.</returns>
		public static float SquaredDistance(Ray r0, Ray r1)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Calculates the distance between two rays.
		/// </summary>
		/// <param name="r0">A <see cref="Ray"/> instance.</param>
		/// <param name="r1">A <see cref="Ray"/> instance.</param>
		/// <returns>Returns the distance between two rays.</returns>
		public static float Distance(Ray r0, Ray r1)
		{
			throw new NotImplementedException();
		}
		#endregion

		#region Segment-Segment
		/// <summary>
		/// Calculates the squared distance between two segments.
		/// </summary>
		/// <param name="s0">A <see cref="Segment"/> instance.</param>
		/// <param name="s1">A <see cref="Segment"/> instance.</param>
		/// <returns>Returns the squared distance between two segments.</returns>
		public static float SquaredDistance(Segment s0, Segment s1)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Calculates the distance between two segments.
		/// </summary>
		/// <param name="s0">A <see cref="Segment"/> instance.</param>
		/// <param name="s1">A <see cref="Segment"/> instance.</param>
		/// <returns>Returns the distance between two segments.</returns>
		public static float Distance(Segment s0, Segment s1)
		{
			throw new NotImplementedException();
		}
		#endregion

        #region Point - Stroke
        public static double Distance(Vector2F[] stroke,Vector2F point,ref int index)
        {
            double len = double.MaxValue;
            double temp_len = 0;int temp_index = 0;
            for (int i = 0; i < stroke.Length; i++)
            {
                temp_len = Distance(stroke[i], point);
                if (temp_len<len)
                {
                    len = temp_len;
                    temp_index = i;
                }
            }
            index = temp_index;
            return len;
        }
        #endregion


    }

 
}
